﻿using System;
using System.Collections.ObjectModel;
using System.Configuration;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Configuration;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using Conducido.WcfMessageLogger.Services.Behaviors.Entities.Config;

namespace Conducido.WcfMessageLogger.Services.Behaviors
{
    /// <summary>
    /// Behavior for logging message sent and (if applicable the response)
    /// </summary>
    public class ServiceMessagelog : BehaviorExtensionElement, IServiceBehavior
    {
        private const string MessageLogTypePropertyName = "WayOfLogging";
        private const string ServiceMessageLogType = "ServiceMessageLogTransportType";
        private const string Uri = "Uri";

        private const string AssemblyNameAtt = "AssemblyName";
        private const string FullyQualifiedTypeNameAtt = "FullyQualifiedTypeName";
        private const string ClassNameAtt = "ClassName";
        private const string PropertyNameAtt = "PropertyName";
        private const string ShouldCorrelateAtt = "ShouldCorrelate";
        private const string HeaderNamespaceAtt = "HeaderNamespace";

        private string _assemblyName;
        private string _className;
        private string _fullyQualifiedTypeName;
        private string _headerNamespace;
        private string _propertyName;
        private ServiceMessageLogType _serviceMessageLogTransportType;
        private bool _shouldCorrelate;
        private string _uri;
        private MessageLogType _wayOfLogging;

        private ServiceMessagelog(string assemblyName, string className, string fullyQualifiedTypeName,
                                  string headerNamespace, string propertyName,
                                  ServiceMessageLogType serviceMessageLogTransportType, bool shouldCorrelate, string uri,
                                  MessageLogType wayOfLogging)
        {
            _assemblyName = assemblyName;
            _className = className;
            _fullyQualifiedTypeName = fullyQualifiedTypeName;
            _headerNamespace = headerNamespace;
            _propertyName = propertyName;
            _serviceMessageLogTransportType = serviceMessageLogTransportType;
            _shouldCorrelate = shouldCorrelate;
            _uri = uri;
            _wayOfLogging = wayOfLogging;
        }

        /// <summary>
        /// Default ctor
        /// </summary>
        public ServiceMessagelog()
        {}

        public override Type BehaviorType
        {
            get { return typeof (ServiceMessagelog); }
        }

        /// <summary>
        /// How should the behavior log
        /// </summary>
        [ConfigurationProperty(MessageLogTypePropertyName, DefaultValue = MessageLogType.Database, IsRequired = true)]
        public MessageLogType WayOfLogging
        {
            get { return (MessageLogType) base[MessageLogTypePropertyName]; }
            set { base[MessageLogTypePropertyName] = value; }
        }

        /// <summary>
        /// Which transport should be used (Only needed if <see cref="MessageLogType.Service"/> has been chosen.
        /// </summary>
        [ConfigurationProperty(ServiceMessageLogType, IsRequired = false)]
        public ServiceMessageLogType ServiceMessageLogTransportType
        {
            get { return (ServiceMessageLogType) base[ServiceMessageLogType]; }
            set { base[ServiceMessageLogType] = value; }
        }

        /// <summary>
        /// Url (also used for connectionstring) 
        /// </summary>
        [ConfigurationProperty(Uri, IsRequired = false)] //TODO:connect this one for the database as well
            public string URI
        {
            get { return (string) base[Uri]; }
            set { base[Uri] = value; }
        }

        /// <summary>
        /// Name of the assembly where the correlationID should be in
        /// </summary>
        [ConfigurationProperty(AssemblyNameAtt, IsRequired = false)]
        public string AssemblyName
        {
            get { return (string) base[AssemblyNameAtt]; }
            set { base[AssemblyNameAtt] = value; }
        }

        /// <summary>
        /// FQN Typename where of the class
        /// </summary>
        [ConfigurationProperty(FullyQualifiedTypeNameAtt, IsRequired = false)]
        public string FullyQualifiedTypeName
        {
            get { return (string) base[FullyQualifiedTypeNameAtt]; }
            set { base[FullyQualifiedTypeNameAtt] = value; }
        }

        /// <summary>
        /// Name of the headerclass
        /// </summary>
        [ConfigurationProperty(ClassNameAtt, IsRequired = false)]
        public string ClassName
        {
            get { return (string) base[ClassNameAtt]; }
            set { base[ClassNameAtt] = value; }
        }

        /// <summary>
        /// The name of the property
        /// </summary>
        [ConfigurationProperty(PropertyNameAtt, IsRequired = false)]
        public string PropertyName
        {
            get { return (string) base[PropertyNameAtt]; }
            set { base[PropertyNameAtt] = value; }
        }

        /// <summary>
        /// Swith to turn it off
        /// </summary>
        [ConfigurationProperty(ShouldCorrelateAtt, IsRequired = false)]
        public bool ShouldCorrelate
        {
            get { return (bool) base[ShouldCorrelateAtt]; }
            set { base[ShouldCorrelateAtt] = value; }
        }

        /// <summary>
        /// Namespace of the header
        /// </summary>
        [ConfigurationProperty(HeaderNamespaceAtt, IsRequired = false)]
        public string HeaderNamespace
        {
            get { return (string) base[HeaderNamespaceAtt]; }
            set { base[HeaderNamespaceAtt] = value; }
        }

        #region IServiceBehavior Members

        public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase,
                                         Collection<ServiceEndpoint> endpoints,
                                         BindingParameterCollection bindingParameters)
        {
            //Not needed
        }

        public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            var messageInspector = new ServiceMessagelogInspector(_wayOfLogging, _serviceMessageLogTransportType, _uri);
            var correlator = new ServiceCorrelation(CreateCorrelationConfiguration());
            foreach (ChannelDispatcher channelDispatcher in serviceHostBase.ChannelDispatchers)
            {
                foreach (EndpointDispatcher endPointDispatcher in channelDispatcher.Endpoints)
                {
                    // let op: de volgorde is belangrijk, de correlator moet als eerste aan de beurt zijn,
                    // anders kunnen de andere inspectors niet hetzelfde correlation id gebruiken.

                    endPointDispatcher.DispatchRuntime.MessageInspectors.Add(correlator);
                    endPointDispatcher.DispatchRuntime.MessageInspectors.Add(messageInspector);
                }
            }
        }

        public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
        }

        #endregion

        protected override object CreateBehavior()
        {
            return new ServiceMessagelog(AssemblyName, ClassName, FullyQualifiedTypeName, HeaderNamespace, PropertyName,
                                         ServiceMessageLogTransportType, ShouldCorrelate, URI, WayOfLogging);
        }

        private CorrelationConfiguration CreateCorrelationConfiguration()
        {
            var config = new CorrelationConfiguration
                             {
                                 AssemblyName = _assemblyName,
                                 ClassName = _className,
                                 FullyQualifiedTypeName = _fullyQualifiedTypeName,
                                 HeaderNamespace = _headerNamespace,
                                 PropertyName = _propertyName,
                                 ShouldCorrelate = _shouldCorrelate
                             };
            return config;
        }
    }
}